home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / edit / xvisrc.zip / FILEIO.C < prev    next >
C/C++ Source or Header  |  1992-07-28  |  15KB  |  648 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)fileio.c    2.1 (Chris & John Downey) 7/29/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     fileio.c
  14. * module function:
  15.     File i/o routines.
  16. * history:
  17.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  18.     Originally by Tim Thompson (twitch!tjt)
  19.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  20.     Heavily modified by Chris & John Downey
  21.  
  22. ***/
  23.  
  24. #include "xvi.h"
  25.  
  26. #ifdef    MEGAMAX
  27. overlay "fileio"
  28. #endif
  29.  
  30. /*
  31.  * Definition of a text file format.
  32.  *
  33.  * This structure may need additional entries to cope with very strange file
  34.  * formats (such as VMS).
  35.  */
  36. struct tfformat
  37. {
  38.     int            tf_eolnchars[2];    /* end of line markers */
  39.     int            tf_eofchar;        /* end of file marker */
  40.     unsigned char   tf_dynamic;        /* autodetect format? */
  41. };
  42.  
  43. /*
  44.  * Names of values for the P_format enumerated parameter.
  45.  *
  46.  * It is essential that these are in the same order as the fmt_...
  47.  * symbolic constants defined in xvi.h.
  48.  */
  49. char    *fmt_strings[] = {
  50.     "cstring",
  51.     "macintosh",
  52.     "msdos",
  53.     "os2",
  54.     "qnx",
  55.     "tos",
  56.     "unix",
  57.     NULL,
  58. };
  59.  
  60. /*
  61.  * Format structures.
  62.  *
  63.  * It is essential that these are in the same order as the fmt_...
  64.  * symbolic constants defined in xvi.h.
  65.  *
  66.  * We don't use '\r' or '\n' to define the end-of-line characters
  67.  * because some compilers interpret them differently & this code has
  68.  * to work the same on all systems.
  69.  */
  70. #define    NOCHAR    EOF
  71.  
  72. static const struct tfformat tftable [] = {
  73.     { { '\0',       NOCHAR    }, EOF,          FALSE },    /* fmt_CSTRING */
  74.     { { CTRL('M'), NOCHAR    }, EOF,          FALSE },    /* fmt_MACINTOSH */
  75.     { { CTRL('M'), CTRL('J')    }, CTRL('Z'), TRUE  },    /* fmt_MSDOS */
  76.     { { CTRL('M'), CTRL('J')    }, CTRL('Z'), TRUE  },    /* fmt_OS2 */
  77.     { { '\036',       NOCHAR    }, EOF,          FALSE },    /* fmt_QNX */
  78.     { { CTRL('M'), CTRL('J')    }, EOF,          TRUE  },    /* fmt_TOS */
  79.     { { CTRL('J'), NOCHAR    }, EOF,          FALSE }    /* fmt_UNIX */
  80. };
  81.  
  82. /*
  83.  * Index of last entry in tftable.
  84.  */
  85. #define    TFMAX    (sizeof tftable / sizeof (struct tfformat) - 1)
  86.  
  87. /*
  88.  * Current text file format.
  89.  */
  90. static struct tfformat curfmt = { { 0, 0 }, 0, FALSE };
  91.  
  92. #define eolnchars    curfmt.tf_eolnchars
  93. #define eofchar        curfmt.tf_eofchar
  94.  
  95. /*
  96.  * Name of current text file format.
  97.  */
  98. static char *fmtname = "INTERNAL ERROR";
  99.  
  100. /*
  101.  * Copy the tftable entry indexed by tfindex into curfmt & update
  102.  * fmtname. Return FALSE if the parameter is invalid, otherwise TRUE.
  103.  *
  104.  * This is called from set_format() (below).
  105.  *
  106.  * Note that we copy a whole tfformat structure here, instead of just copying
  107.  * a pointer. This is so that curfmt.eolnchars & curfmt.eofchar will compile
  108.  * to absolute address references instead of indirections, which should be
  109.  * significantly more efficient because they are referenced for every
  110.  * character we read or write.
  111.  */
  112. static bool_t
  113. txtformset(tfindex)
  114. int    tfindex;
  115. {
  116.     if (tfindex < 0 || tfindex > TFMAX)
  117.     return FALSE;
  118.     (void) memcpy((char *) &curfmt, (const char *) &tftable[tfindex],
  119.                           sizeof curfmt);
  120.     fmtname = fmt_strings[tfindex];
  121.     return TRUE;
  122. }
  123.  
  124. /*
  125.  * Check value of P_format parameter.
  126.  */
  127. bool_t
  128. set_format(window, new_value, interactive)
  129. Xviwin    *window;
  130. Paramval new_value;
  131. bool_t    interactive;
  132. {
  133.     if (!txtformset(new_value.pv_i)) {
  134.     if (interactive) {
  135.         show_error(window, "Invalid text file format (%d)",
  136.                             new_value.pv_i);
  137.     }
  138.     return(FALSE);
  139.     }
  140.     return(TRUE);
  141. }
  142.  
  143. /*
  144.  * Find out if there's a format we know about with the single specified
  145.  * end-of-line character. If so, change to it.
  146.  */
  147. static bool_t
  148. eolnhack(c)
  149.     register int    c;
  150. {
  151.     register int    tfindex;
  152.  
  153.     for (tfindex = 0; tfindex <= TFMAX; tfindex++) {
  154.     register const int    *eolp;
  155.  
  156.     eolp = tftable[tfindex].tf_eolnchars;
  157.     if (eolp[0] == c && eolp[1] == NOCHAR) {
  158.         (void) txtformset(tfindex);
  159.         set_param(P_format, tfindex, (char **) NULL);
  160.         P_setchanged(P_format);
  161.         return TRUE;
  162.     }
  163.     }
  164.     return FALSE;
  165. }
  166.  
  167. /*
  168.  * Read in the given file, filling in the given "head" and "tail"
  169.  * arguments with pointers to the first and last elements of the
  170.  * linked list of Lines; if nothing was read, both pointers are set to
  171.  * NULL. The return value is the number of lines read, if successful
  172.  * (this can be 0 for an empty file), or an error return code, which
  173.  * can be gf_NEWFILE, gf_CANTOPEN, gf_IOERR or gf_NOMEM.
  174.  *
  175.  * If there is an error, such as not being able to read the file or
  176.  * running out of memory, an error message is printed; otherwise, a
  177.  * statistics line is printed using show_message().
  178.  *
  179.  * The "extra_str" string is printed just after the filename in the
  180.  * displayed line, and is typically used for "Read Only" messages. If
  181.  * the file doesn't appear to exist, the filename is printed again,
  182.  * immediately followed by the "no_file_str" string, & we return
  183.  * gf_NEWFILE.
  184.  */
  185. long
  186. get_file(window, filename, headp, tailp, extra_str, no_file_str)
  187. Xviwin        *window;
  188. char        *filename;
  189. Line        **headp;
  190. Line        **tailp;
  191. char        *extra_str;
  192. char        *no_file_str;
  193. {
  194.     register FILE    *fp;        /* ptr to open file */
  195. #ifndef i386
  196.     register
  197. #endif
  198.     unsigned long    nchars;        /* number of chars read */
  199.     unsigned long    nlines;        /* number of lines read */
  200.     unsigned long    nulls;        /* number of null chars */
  201.     unsigned long    toolong;    /*
  202.                      * number of lines
  203.                      * which were too long
  204.                      */
  205.     bool_t        incomplete;    /* incomplete last line */
  206.     Line        *lptr = NULL;    /* pointer to list of lines */
  207.     Line        *last = NULL;    /*
  208.                      * last complete line
  209.                      * read in
  210.                      */
  211.     Line        *lp;        /*
  212.                      * line currently
  213.                      * being read in
  214.                      */
  215.     register enum {
  216.     at_soln,
  217.     in_line,
  218.     got_eolnc0,
  219.     at_eoln,
  220.     at_eof
  221.     }            state;
  222.     register char    *buff;        /*
  223.                      * text of line
  224.                      * being read in
  225.                      */
  226.     register int    col;        /* current column in line */
  227.  
  228.     if (P_ischanged(P_format)) {
  229.     show_message(window, "\"%s\" [%s]%s", filename, fmtname, extra_str);
  230.     } else {
  231.     show_message(window, "\"%s\"%s", filename, extra_str);
  232.     }
  233.  
  234.     fp = fopenrb(filename);
  235.     if (fp == NULL) {
  236.     *headp = *tailp = NULL;
  237.     if (exists(filename)) {
  238.         show_error(window, "Can't read \"%s\"", filename);
  239.         return(gf_CANTOPEN);
  240.     } else {
  241.         show_message(window, "\"%s\"%s", filename, no_file_str);
  242.         return(gf_NEWFILE);
  243.     }
  244.     }
  245.  
  246. #ifdef    SETVBUF_AVAIL
  247.     {
  248.     unsigned int    bufsize;
  249.  
  250.     bufsize = READBUFSIZ;
  251.  
  252.     /*
  253.      * Keep trying to set the buffer size to something
  254.      * large, reducing the size by 1/2 each time.
  255.      * This will eventually work, and will not usually
  256.      * take very many calls. (jmd)
  257.      */
  258.     while (setvbuf(fp, (char *) NULL, _IOFBF, bufsize) != 0 &&
  259.                         bufsize > 1) {
  260.         bufsize /= 2;
  261.     }
  262.     }
  263. #endif /* SETVBUF_AVAIL */
  264.  
  265.     nchars = nlines = nulls = toolong = 0;
  266.     col = 0;
  267.     incomplete = FALSE;
  268.     state = at_soln;
  269.     while (state != at_eof) {
  270.  
  271.     register int    c;
  272.  
  273.     c = getc(fp);
  274.  
  275.     if (c == EOF || c == eofchar) {
  276.         if (state != at_soln) {
  277.         /*
  278.          * Reached EOF in the middle of a line; what
  279.          * we do here is to pretend we got a properly
  280.          * terminated line, and assume that a
  281.          * subsequent getc will still return EOF.
  282.          */
  283.         incomplete = TRUE;
  284.         state = at_eoln;
  285.         } else {
  286.         state = at_eof;
  287.         break;
  288.         }
  289.     } else {
  290.         nchars++;
  291.  
  292.         switch (state) {
  293.         case at_soln:
  294.         /*
  295.          * We're at the start of a line, &
  296.          * we've got at least one character,
  297.          * so we have to allocate a new Line
  298.          * structure.
  299.          *
  300.          * If we can't do it, we throw away
  301.          * the lines we've read in so far, &
  302.          * return gf_NOMEM.
  303.          */
  304.         if ((lp = newline(MAX_LINE_LENGTH)) == NULL) {
  305.             if (lptr != NULL) {
  306.             throw(lptr);
  307.             }
  308.             (void) fclose(fp);
  309.             *headp = *tailp = NULL;
  310.             return(gf_NOMEM);
  311.         } else {
  312.             buff = lp->l_text;
  313.         }
  314.         case in_line:
  315.         if (c == eolnchars[0]) {
  316.             if (eolnchars[1] == NOCHAR) {
  317.             state = at_eoln;
  318.             } else {
  319.             state = got_eolnc0;
  320.             continue;
  321.             }
  322.         } else if (c == eolnchars [1] && curfmt.tf_dynamic &&
  323.                          eolnhack(c)) {
  324.             /*
  325.              * If we get the second end-of-line
  326.              * marker, but not the first, see if
  327.              * we can accept th